﻿using System;
using System.Collections.Generic;

using AForge.Neuro;
using AForge.Neuro.Learning;
using UnityEngine;


namespace AForgeFramework
{

    public class RNA
    {
        #region Atributos

        public double Erro { get; set; }

        private ActivationNetwork _network;
        private BackPropagationLearning _teacher;

        private double[][] _input, _output;
        private int _epoch;
        private double _maxNumber;

        public int MaxEpochs { get; set; }
        public double MinError { get; set; }

        #endregion

        #region Construtor

        /// <summary>
        /// Realiza o treinamento
        /// </summary>
        public RNA(double[][] input, double[][] output, int inputCounts, params int[] neuronCount)
        {
            _network = new ActivationNetwork(new BipolarSigmoidFunction(), inputCounts, neuronCount);

            _network.Randomize();

            _teacher = new BackPropagationLearning(_network);
            _teacher.LearningRate = 0.2f;
            _teacher.Momentum = 0.4f;

            _input = input;
            _output = output;

            MinError = 0.0001;
            MaxEpochs = 5000;

            Normalize();
        }

        #endregion

        #region Métodos

        private void Normalize()
        {
         
            for (int i = 0; i < _input.Length; i++)
            {
                for (int k = 0; k < _input[i].Length; k++)
                {
                    if (_maxNumber < _input[i][k])
                    {
                        _maxNumber = _input[i][k];
                    }
                }
            }
            for (int i = 0; i < _output.Length; i++)
            {
                for (int j = 0; j < _output[i].Length; j++)
                {
                    if (_maxNumber < _output[i][j])
                    {
                        _maxNumber = _output[i][j];
                    }
                }
            }

            for (int i = 0; i < _input.Length; i++)
            {
                for (int j = 0; j < _input[i].Length; j++)
                {
                    _input[i][j] = _input[i][j] / _maxNumber;
                }
            }


            for (int i = 0; i < _output.Length; i++)
            {
                for (int j = 0; j < _output[i].Length; j++)
                {
                    _output[i][j] = _output[i][j] / _maxNumber;
                }
            }
        }

        public void TrainNetWork()
        {
            _epoch = 0;
            Erro = 0;
            do
            {
                Erro = _teacher.RunEpoch(_input, _output);
                _epoch++;

            } while (Erro > MinError && _epoch < MaxEpochs);

        }


        /// <summary>
        /// Realiza o reconhecimento
        /// </summary>
        public double Recognize(double value)
        {
            double[] input = new double[1];
            input[0] = value / _maxNumber;
            double[] saida = _network.Compute(input);

            return (_network.Compute(input)[0]) * _maxNumber;
        }

        public double Recognize(double[] values)
        {
            double[] input = new double[values.Length];

            for (int i = 0; i < values.Length; i++)
            {
                input[i] = values[i] / _maxNumber;
            }

            double[] saida = _network.Compute(input);
           
            return (_network.Compute(input)[0]) * _maxNumber;
        }

        #endregion
    }
}
